/*
 * Copyright 2002-2023 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.web.util.pattern;

import org.springframework.http.server.PathContainer;
import org.springframework.util.StringUtils;

/**
 * PathPatternParser 和 AntPathMatcher 是 Spring 框架中用于路径匹配的两个不同工具，它们在 Spring MVC 中扮演着重要的角色。
 * 两者都提供了将 URL 路径模式与实际请求路径进行匹配的能力，但它们的工作方式和适用场景有所不同。以下是关于 PathPatternParser 和 AntPathMatcher 的详细比较：
 *
 * 1. 设计目的
 * 		AntPathMatcher：这是 Spring 早期版本中的默认路径匹配器，主要用于支持 Ant 风格的路径模式。它广泛应用于 Spring MVC 中，用来匹配控制器方法上的 @RequestMapping 注解所定义的 URL 模式。
 * 		PathPatternParser：这是 Spring 5.3 引入的新路径匹配器，旨在提供更高效、更灵活的路径匹配能力。它基于 RFC 6570 URI 模板规范，并且是 Spring MVC 默认的路径解析器（从 Spring 5.3 开始）。
 *
 * 2. 路径模式语法
 * 		AntPathMatcher：
 * 			使用通配符 * 匹配任意数量的字符，直到遇到斜杠。
 * 			使用双星号 ** 匹配任意数量的字符，包括斜杠。
 * 			支持 {spring:[a-z]+} 这样的占位符，用于路径变量捕获。
 * 		PathPatternParser：
 * 			支持 RFC 6570 URI 模板风格的路径模式，例如 /users/{id}。
 * 			提供了更严格的路径变量捕获机制，如 {varName}。
 * 			不支持通配符 * 和 **，而是使用 {*path} 或 {**path} 来实现类似的功能。
 * 			支持矩阵变量（Matrix Variables），即路径段后面的键值对，例如 /cars;year=2020;color=red。
 * 3. 性能
 * 			AntPathMatcher：由于其设计较为简单，对于大多数情况来说性能是可以接受的。然而，在处理复杂的路径模式时可能会显得不够高效。
 * 			PathPatternParser：相较于 AntPathMatcher，它具有更好的性能表现，尤其是在处理大量或复杂路径模式的情况下。
 * 			这是因为 PathPatternParser 采用了更优化的算法来进行路径匹配。
 *
 * 4. 灵活性与功能
 * 		AntPathMatcher：虽然功能强大，但在某些高级特性方面有所欠缺，比如对矩阵变量的支持有限。
 * 		PathPatternParser：提供了更多的灵活性和功能，例如对 URI 模板的支持、更丰富的路径变量捕获选项以及对矩阵变量的支持等。
 *
 * 5. 配置与使用
 * 		AntPathMatcher：作为 Spring MVC 的传统组件，它已经被广泛使用并且很容易集成到现有项目中。如果你的应用程序已经在使用 AntPathMatcher 并且没有特别的需求，那么继续使用它是合理的。
 * 		PathPatternParser：从 Spring 5.3 开始成为默认选择。如果你正在开发新的应用程序或者想要利用最新的路径匹配功能，推荐使用 PathPatternParser。
 * 		你可以在应用配置中通过设置 spring.mvc.pathmatch.matching-strategy 属性来切换路径匹配策略。
 *
 * 		# 使用 PathPatternParser (默认)
 * 		spring.mvc.pathmatch.matching-strategy=PATH_PATTERN
 *
 * 		# 使用 AntPathMatcher
 * 		spring.mvc.pathmatch.matching-strategy=ANT_PATH_MATCHER
 *
 *基本用法
 * 		1）创建 PathPatternParser 实例
 * 			你可以直接实例化 PathPatternParser 类来开始解析路径模式：
 * 			import org.springframework.web.util.pattern.PathPatternParser;
 * 			import org.springframework.web.util.pattern.PathPattern;
 *
 * 			PathPatternParser parser = new PathPatternParser();
 * 			PathPattern pattern = parser.parse("/resources/{name}.*");
 *
 * 		2）匹配路径
 *		 一旦你有了 PathPattern 对象，就可以用它来测试给定的路径是否与模式匹配：
 * 		boolean matches = pattern.matches("resources/data.json"); // 返回 true
 * 		boolean notMatches = pattern.matches("other/data.json"); // 返回 false
 *
 *
 * <br>
 * Parser for URI path patterns producing {@link PathPattern} instances that can
 * then be matched to requests.
 * <br> 用于 URI 路径模式的解析器，可以生成 {@link PathPattern} 实例，然后与请求进行匹配。
 *
 * <p>The {@link PathPatternParser} and {@link PathPattern} are specifically
 * designed for use with HTTP URL paths in web applications where a large number
 * of URI path patterns, continuously matched against incoming requests,
 * motivates the need for efficient matching.
 * <p> {@link PathPatternParser} 和 {@link PathPattern} 专门设计用于 Web 应用中的 HTTP URL
 * 路径，其中大量的 URI 路径模式连续地与传入请求进行匹配，这促使了对高效匹配的需求。
 *
 * <p>For details of the path pattern syntax see {@link PathPattern}.
 *
 * @author Andy Clement
 * @since 5.0
 */
public class PathPatternParser {

	private boolean matchOptionalTrailingSeparator = false;

	private boolean caseSensitive = true;

	private PathContainer.Options pathOptions = PathContainer.Options.HTTP_PATH;


	/**
	 * Configure whether a {@link PathPattern} produced by this parser should
	 * automatically match request paths with a trailing slash.
	 * <p>If set to {@code true} a {@code PathPattern} without a trailing slash
	 * will also match request paths with a trailing slash. If set to
	 * {@code false} a {@code PathPattern} will only match request paths with
	 * a trailing slash.
	 * <p>The default was changed in 6.0 from {@code true} to {@code false} in
	 * order to support the deprecation of the property.
	 * @deprecated transparent support for trailing slashes is deprecated as of
	 * 6.0 in favor of configuring explicit redirects through a proxy,
	 * Servlet/web filter, or a controller.
	 */
	@Deprecated(since = "6.0")
	public void setMatchOptionalTrailingSeparator(boolean matchOptionalTrailingSeparator) {
		this.matchOptionalTrailingSeparator = matchOptionalTrailingSeparator;
	}

	/**
	 * Whether optional trailing slashing match is enabled.
	 * @deprecated as of 6.0 together with {@link #setMatchOptionalTrailingSeparator(boolean)}.
	 */
	@Deprecated(since = "6.0")
	public boolean isMatchOptionalTrailingSeparator() {
		return this.matchOptionalTrailingSeparator;
	}

	/**
	 * Configure whether path pattern matching should be case-sensitive.
	 * <p>The default is {@code true}.
	 */
	public void setCaseSensitive(boolean caseSensitive) {
		this.caseSensitive = caseSensitive;
	}

	/**
	 * Whether case-sensitive pattern matching is enabled.
	 */
	public boolean isCaseSensitive() {
		return this.caseSensitive;
	}

	/**
	 * Set options for parsing patterns. These should be the same as the
	 * options used to parse input paths.
	 * <p>{@link org.springframework.http.server.PathContainer.Options#HTTP_PATH}
	 * is used by default.
	 * @since 5.2
	 */
	public void setPathOptions(PathContainer.Options pathOptions) {
		this.pathOptions = pathOptions;
	}

	/**
	 * Get the {@link #setPathOptions configured} pattern parsing options.
	 * @since 5.2
	 */
	public PathContainer.Options getPathOptions() {
		return this.pathOptions;
	}


	/**
	 * Prepare the given pattern for use in matching to full URL paths.
	 * <p>By default, prepend a leading slash if needed for non-empty patterns.
	 * @param pattern the pattern to initialize
	 * @return the updated pattern
	 * @since 5.2.25
	 */
	public String initFullPathPattern(String pattern) {
		return (StringUtils.hasLength(pattern) && !pattern.startsWith("/") ? "/" + pattern : pattern);
	}

	/**
	 * Process the path pattern content, a character at a time, breaking it into
	 * path elements around separator boundaries and verifying the structure at each
	 * stage. Produces a PathPattern object that can be used for fast matching
	 * against paths. Each invocation of this method delegates to a new instance of
	 * the {@link InternalPathPatternParser} because that class is not thread-safe.
	 * @param pathPattern the input path pattern, e.g. /project/{name}
	 * @return a PathPattern for quickly matching paths against request paths
	 * @throws PatternParseException in case of parse errors
	 */
	public PathPattern parse(String pathPattern) throws PatternParseException {
		return new InternalPathPatternParser(this).parse(pathPattern);
	}


	/**
	 * Shared, read-only instance of {@code PathPatternParser}.
	 * <p>Uses default settings:
	 * <ul>
	 * <li>{@code matchOptionalTrailingSeparator = false}
	 * <li>{@code caseSensitive = true}
	 * <li>{@code pathOptions = PathContainer.Options.HTTP_PATH}
	 * </ul>
	 */
	public final static PathPatternParser defaultInstance = new PathPatternParser() {

		@SuppressWarnings("deprecation")
		@Override
		public void setMatchOptionalTrailingSeparator(boolean matchOptionalTrailingSeparator) {
			raiseError();
		}

		@Override
		public void setCaseSensitive(boolean caseSensitive) {
			raiseError();
		}

		@Override
		public void setPathOptions(PathContainer.Options pathOptions) {
			raiseError();
		}

		private void raiseError() {
			throw new UnsupportedOperationException(
					"This is a read-only, shared instance that cannot be modified");
		}

	};

}
